#pragma once

#include <boost/noncopyable.hpp>
#include "tests.core.dual.h"
#include <vex/core/make_raw_handle.h>

namespace vex {
    namespace core { namespace detail {
        struct implement_helper {
            template<class TImpl, class TContract> bool query_interface_impl(TContract** c)
            {
                if (c) {
                    *c = static_cast<TImpl*>(this);
                    return true;
                }
                return false;
            }

            template<class TImpl, class TContract> bool query_handle_impl(::vex::handle<TContract>** h)
            {
                if (h) {
                    *h = static_cast<TImpl*>(this);
                    (*h)->add_ref();
                    return true;
                }
                return false;
            }
        };

        // primary template, base list non-empty and non-final
        template<class TImpl, class TBaseListHead, class TBaseListTail>
        struct VEX_ABSTRACT implement_bases
            : public ::vex::handle<TBaseListHead>
            , public implement_bases<
            TImpl, 
            typename TBaseListTail::head_type, 
            typename TBaseListTail::tail_type
            >
        {
            typedef implement_bases<TImpl, TBaseListHead, TBaseListTail> this_type;

            typedef implement_bases<
                TImpl, 
                typename TBaseListTail::head_type, 
                typename TBaseListTail::tail_type
            > base_type;

            using base_type::query_handle;
            using base_type::query_interface;

            virtual bool query_handle(::vex::handle<TBaseListHead>** p_handle)
            {
                return static_cast<TImpl*>(this)->query_handle_impl<TImpl>(p_handle); 
            }

            virtual bool query_interface(TBaseListHead** p_contract)
            {
                return static_cast<TImpl*>(this)->query_interface_impl<TImpl>(p_contract);
            }
        };

        /// specialization, head and tail empty
        template<class TImpl>
        struct VEX_ABSTRACT implement_bases< TImpl, ::vex::core::detail::nothing, ::vex::core::detail::nothing>
        {};

        /// specialization, base list empty, final
        template<class TImpl, class TBase>
        struct VEX_ABSTRACT implement_bases< TImpl, TBase, ::vex::extends<> >
            : public ::vex::handle<TBase>
        {
            virtual bool query_handle(::vex::handle<TBase>** p_handle)
            {
                return static_cast<TImpl*>(this)->query_handle_impl<TImpl>(p_handle); 
            }

            virtual bool query_interface(TBase** p_contract)
            {
                return static_cast<TImpl*>(this)->query_interface_impl<TImpl>(p_contract);
            }
        };

        /// Primary template, implements T and recurses into base list
        template<class TImpl, class T, class TBaseList = typename T::base_list_type>
        struct VEX_ABSTRACT implement_contract_and_bases
            : public T
            , public ::vex::handle<T>
            , public implement_helper
            , public implement_bases<
            TImpl, 
            typename TBaseList::head_type, 
            typename TBaseList::tail_type
            >
        {
            typedef implement_bases<
                TImpl, 
                typename TBaseList::head_type, 
                typename TBaseList::tail_type
            > base_type;

            using base_type::query_handle;
            using base_type::query_interface;

            virtual bool query_handle(::vex::handle<T>** p_handle)
            {
                return this->query_handle_impl<TImpl>(p_handle); 
            }

            virtual bool query_interface(T** p_contract)
            {
                return this->query_interface_impl<TImpl>(p_contract);
            }
        };

        /// Specialization, base list is empty (= extends<>)
        template< class TImpl, class T >
        struct VEX_ABSTRACT implement_contract_and_bases< TImpl, T, ::vex::extends<> >
            : public T
            , public ::vex::handle<T>
            , public implement_helper
        {
            virtual bool query_handle(::vex::handle<T>** p_handle)
            {
                return this->query_handle_impl<TImpl>(p_handle); 
            }

            virtual bool query_interface(T** p_contract)
            {
                return this->query_interface_impl<TImpl>(p_contract);
            }
        };
    }}

    template<
        class T1,
        class T2 = ::vex::core::detail::nothing,
        class T3 = ::vex::core::detail::nothing,
        class T4 = ::vex::core::detail::nothing,
        class T5 = ::vex::core::detail::nothing,
        class T6 = ::vex::core::detail::nothing,
        class T7 = ::vex::core::detail::nothing
    >
    struct VEX_ABSTRACT implements
        : public ::vex::core::detail::implement_contract_and_bases<
            implements<T1, T2, T3, T4, T5, T6, T7>,
            T1,
            typename T1::base_list_type
        >
        , public implements<
            T2, T3, T4, T5, T6, T7
        >
    {
        typedef ::vex::core::detail::implement_contract_and_bases<
            implements<T1, T2, T3, T4, T5, T6, T7>,
            T1,
            typename T1::base_list_type
        > base_type;

        typedef T1 head_type;
        typedef implements<T2, T3, T4, T5, T6, T7> tail_type;
    };

    template<>
    struct VEX_ABSTRACT implements<::vex::core::detail::nothing>
    {
        typedef ::vex::core::detail::nothing head_type;
        typedef ::vex::core::detail::nothing tail_type;
    };

    template< class T, class TImplements >
    struct VEX_ABSTRACT define_implementation 
        : public TImplements
    {
        typedef T impl_type;
        typedef TImplements base_list_type;
    };    

    template <class TBase> auto make_handle()
        -> typename vex::core::handle_from_base<TBase>*
    {
        try {
            return new vex::core::handle_from_base<TBase>;
        } catch (std::exception &) {
            return nullptr;
        }
    }
}
